from lib.mongo import MongoDB
from lib.redis import Redis
from typing import Dict
import asyncio
import traceback
import time
from lib import singleton


@singleton
class MongoLogSave(object):
    def __init__(self, mdb: MongoDB, redis: Redis):
        self.mdb = mdb
        self.redis = redis
        self.db_name = "model_log"
        self.lock_id_set = set()
        self.is_close = False
        self.limit = 10000  # 单次最大处理数
        self.class_name = type(self).__name__

    async def run(self):
        print(f"start write mongo log！！")
        while not self.is_close:
            await asyncio.sleep(1)
            await self.save_all()

    def close(self):
        self.is_close = True

    # 保存所有玩家数据
    async def save_all(self, auto: bool = True) -> None:
        try:
            log_list = await self.mdb.find(self.db_name, {"err": {"$exists": False}}, limit=self.limit)
        except Exception as e:
            print(f"{self.class_name} save_all err ->{e}")
            return

        if not log_list:
            return

        for log_info in log_list:
            # 停服了 停止写入，交给停服逻辑写入
            if self.is_close and auto:
                break
            await self.write_log(log_info, "auto")

        self.lock_id_set = set()

        if not auto:
            print(f"{self.class_name} save_all success！！log_len--->{len(log_list)}")

    # 写入
    async def write_log(self, log: Dict, act: str) -> None:
        """
        :param log:  要写入的日志
        :param act: auto：系统自动写入  play玩家离线写入
        """
        _writeId, _writeUid = log["_id"], log.get("uid")
        # 正在被处理
        if _writeId in self.lock_id_set:
            return

        # lock
        self.lock_id_set.add(_writeId)

        _err, _writeErr = None, None
        try:
            await getattr(self.mdb, log["act"])(log["db_name"], *log["data_list"])
        except Exception as e:
            err, _writeErr = str(traceback.format_exc()), e
            print(f"{self.class_name} write_log error!! ==> user: {act} act: 【{log['act']}】 args: 【{log['data_list']}】-->{e}")

        try:
            # 记录报错的
            if _writeErr:
                await self.mdb.update(self.db_name, {"_id": _writeId}, {"err": str(_writeErr)})
            # 删掉已写库的
            else:
                await self.mdb.delete(self.db_name, {"_id": _writeId})
        except Exception as e:
            print(f"{self.class_name} flag error!! act: {act} err-->{e}")

        # # un lock
        # self.lock_id_set.discard(_writeId)

    # 保存玩家相关数据
    async def save_by_uid(self, uid: str, bind_uid: str) -> None:
        try:
            log_list = await self.mdb.find(self.db_name, {"uid": uid, "err": {"$exists": False}})
        except Exception as e:
            print(f"{self.class_name} save_by_uid mongo err->{e}")
            return

        if not log_list:
            return

        _mcKey = f"{bind_uid}_model_save"
        # set login lock
        await self.redis.set(_mcKey, int(time.time()), ex=60)

        for log_info in log_list:
            await self.write_log(log_info, "play")

        # unlock
        await self.redis.delete(_mcKey)

        print(f"{bind_uid}退出游戏，成功处理{len(log_list)}")
